/*
    Copyright (C) 2019 Zongming Liu <1849059316@qq.com>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef Object_h__
#define Object_h__

#include <cassert>
#include <list>
#include <string>
#include "../base/Debuger.h"

namespace SkyEngine2d
{
    template <typename T>
    class ObjectProxy
    {
    public:
        virtual ~ObjectProxy() {}

        void exit(T *target)
        {
            this->onExit(target);
        }

        void enter(T *target)
        {
            this->onEnter(target);
        }

    protected:
        virtual void onEnter(T *target) = 0;
        virtual void onExit(T *target) = 0;
    };

    template <typename T>
    class Object
    {
    public:
        virtual ~Object();

        void addChildren(T *children);

        void addChildren(T *children, const std::wstring &name);

        void removeChildren(T *children, bool is_clear = true);

        void setParent(T *parent);

        T *getParent() const;

        virtual bool init();

        void setName(const std::wstring &name);

        const std::wstring &getName() const;

        ObjectProxy<T> *getProxy() const;

        void setProxy(ObjectProxy<T> *proxy);

    protected:
        Object(T *parent = nullptr);

        T *m_parent;
        std::list<T *> m_childrenList;

        std::wstring m_name;

        ObjectProxy<T> *m_proxy;

    private:
        void detachChilder(T *children);

    private:
        T *m_self;
    };

    template <typename T>
    void SkyEngine2d::Object<T>::setProxy(ObjectProxy<T> *proxy)
    {
        if (m_proxy)
        {
            delete m_proxy;
            m_proxy = nullptr;
        }

        m_proxy = proxy;
    }

    template <typename T>
    SkyEngine2d::ObjectProxy<T> *SkyEngine2d::Object<T>::getProxy() const
    {
        return m_proxy;
    }

    template <typename T>
    void SkyEngine2d::Object<T>::addChildren(T *children, const std::wstring &name)
    {
        this->setName(name);
        this->addChildren(children);
    }

    template <typename T>
    const std::wstring &SkyEngine2d::Object<T>::getName() const
    {
        return m_name;
    }

    template <typename T>
    void SkyEngine2d::Object<T>::setName(const std::wstring &name)
    {
        this->m_name = name;
    }

    template <typename T>
    bool Object<T>::init()
    {
        T *obj = dynamic_cast<T *>(this);
        DebugerAssert(obj, "节点指针转换失败");
        m_self = obj;
        return true;
    }

    template <typename T>
    void Object<T>::addChildren(T *children)
    {
        m_childrenList.push_back(children);
        if (m_proxy)
        {
            m_proxy->enter(children);
        }
    }

    template <typename T>
    void Object<T>::removeChildren(T *children, bool is_clear)
    {
        this->detachChilder(children);

        if (is_clear)
        {
            delete children;
            return;
        }
    }

    template <typename T>
    void SkyEngine2d::Object<T>::detachChilder(T *children)
    {
        children->m_parent = nullptr;
        m_childrenList.remove(children);
        auto proxy = children->getProxy();
        if (proxy)
        {
            proxy->exit(children);
        }
    }

    template <typename T>
    void Object<T>::setParent(T *parent)
    {
        if (m_parent)
        {
            m_parent->removeChildren(m_self, false);
        }

        m_parent = parent;

        if (parent)
        {
            parent->addChildren(m_self);
        }
    }

    template <typename T>
    T *Object<T>::getParent() const
    {
        return m_parent;
    }

    template <typename T>
    Object<T>::Object(T *parent /*= nullptr*/)
        : m_parent(parent), m_self(nullptr), m_proxy(nullptr)
    {
    }

    template <typename T>
    Object<T>::~Object()
    {
        if (m_parent)
        {
            m_parent->removeChildren(m_self, false);
        }

        auto temp = m_childrenList;

        for (auto &p : temp)
        {
            assert(p);
            if (p)
            {
                delete p;
            }
        }

        if (m_proxy)
        {
            delete m_proxy;
            m_proxy = nullptr;
        }

        // DebugInfor("release node:%s", m_name.c_str());
    }

}

#endif // Object_h__
